package show.lmm.demo.springboot_aot;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

@Slf4j
public class AotDemoRuntimeHints implements RuntimeHintsRegistrar {

    @SneakyThrows
    @Nullable
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
        PathMatchingResourcePatternResolver pathMatching = new PathMatchingResourcePatternResolver(classLoader);
        registerAotPackage(bindingRegistrar, pathMatching, hints.reflection(), "show.lmm.demo.springboot_aot.view");
        hints.resources().registerPattern("config/*.txt");
    }

    /**
     * 注册AOT包
     */
    private void registerAotPackage(BindingReflectionHintsRegistrar bindingRegistrar,
                                    PathMatchingResourcePatternResolver pathMatching,
                                    ReflectionHints hints,
                                    String packageName
    ) throws Exception {
        CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
        String resourcePath = packageName.replaceAll("\\.", "/");
        resourcePath = String.format("classpath*:%s/**/*.class", resourcePath);
        Resource[] resources = pathMatching.getResources(resourcePath);
        for (Resource resource : resources) {
            String filename = resource.getFilename();
            if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
                // Ignore CGLIB-generated classes in the classpath
                continue;
            }
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String className = metadataReader.getClassMetadata().getClassName();
            // 手动aot
            Class<?> classObj = Class.forName(className);
            bindingRegistrar.registerReflectionHints(hints, classObj);
        }
    }
}
